#include "znx_conn.h"
#include "comm/znx_handles.h"


static inline znx_bool_t
znx_conn_local_address_init(znx_conn_t *conn)
{
    znx_address_t           address;
    socklen_t               address_len;
    znx_result_t            ret;
    int                     n;

    address_len = sizeof(znx_address_t);
    ret = znx_syscall_getsockname(conn->fd,
            (znx_sockaddr_t *) &address, &address_len);
    if (ret < 0) {
        return ZNX_FALSE;
    }

    conn->local_address = znx_pool_malloc(conn->pool, sizeof(znx_address_t));
    znx_memcpy(conn->local_address, &address, address_len);

    conn->local_address_text.data = znx_pool_malloc(conn->pool, ZNX_SOCKADDR_SIZE);
    n = znx_address_to_str(&address,
        conn->local_address_text.data, ZNX_SOCKADDR_SIZE);
    // n == 0 mean failed, but we return ZNX_TRUE.
    conn->local_address_text.len = (size_t)n;

    return ZNX_TRUE;
}


static inline znx_bool_t
znx_conn_peer_address_init(znx_conn_t *conn)
{
    znx_address_t           address;
    socklen_t               address_len;
    znx_result_t            ret;
    int                     n;

    address_len = sizeof(znx_address_t);
    ret = znx_syscall_getpeername(conn->fd,
            (znx_sockaddr_t *) &address, &address_len);
    if (ret < 0) {
        return ZNX_FALSE;
    }

    conn->peer_address = znx_pool_malloc(conn->pool, sizeof(znx_address_t));
    znx_memcpy(conn->peer_address, &address, address_len);

    conn->peer_address_text.data = znx_pool_malloc(conn->pool, ZNX_SOCKADDR_SIZE);
    n = znx_address_to_str(&address,
        conn->peer_address_text.data, ZNX_SOCKADDR_SIZE);
    // n == 0 mean failed, but we return ZNX_TRUE.
    conn->peer_address_text.len = (size_t)n;

    return ZNX_TRUE;
}


znx_address_t *
znx_conn_local_address(znx_conn_t *conn)
{
    if (conn->local_address) {
        return conn->local_address;
    }

    if (conn->fd == INVALID_FD) {
        return NULL;
    }

    if (!znx_conn_local_address_init(conn)) {
        return NULL;
    }

    return conn->local_address;
}


znx_str_t *
znx_conn_local_address_text(znx_conn_t *conn)
{
    if (conn->local_address != NULL) {
        return &conn->local_address_text;
    }

    if (conn->fd == INVALID_FD) {
        return NULL;
    }

    if (!znx_conn_local_address_init(conn)) {
        return NULL;
    }

    return &conn->local_address_text;
}


znx_address_t *
znx_conn_peer_address(znx_conn_t *conn)
{
    if (conn->peer_address != NULL) {
        return conn->peer_address;
    }

    if (conn->fd == INVALID_FD) {
        return NULL;
    }

    if (!znx_conn_peer_address_init(conn)) {
        return NULL;
    }

    return conn->peer_address;
}


znx_str_t *
znx_conn_peer_address_text(znx_conn_t *conn)
{
    if (conn->peer_address != NULL) {
        return &conn->peer_address_text;
    }

    if (conn->fd == INVALID_FD) {
        return NULL;
    }

    if (!znx_conn_peer_address_init(conn)) {
        return NULL;
    }

    return &conn->peer_address_text;
}


znx_result_t
znx_conn_read(znx_conn_t *conn, void *buf, size_t count)
{
    znx_result_t        ret;

    ret = znx_syscall_read(conn->fd, buf, count);

    if (ret > 0) {
        conn->recv_bytes += (size_t)ret;
        return ret;
    }

    conn->read.ready = 0;

    if (ret == 0) {
        conn->read.eof = 1;
        return ret;
    }

    if (-ret == EAGAIN) {
        return ZNX_AGAIN;
    }

    conn->read.error = 1;
    return ret;
}


znx_result_t
znx_conn_readv(znx_conn_t *conn, znx_iovec_t *vector, int count)
{
    znx_result_t        ret;

    ret = znx_syscall_readv(conn->fd, vector, count);

    if (ret > 0) {
        conn->recv_bytes += (size_t)ret;
        return ret;
    }

    conn->read.ready = 0;

    if (ret == 0) {
        conn->read.eof = 1;
        return ret;
    }

    if (-ret == EAGAIN) {
        return ZNX_AGAIN;
    }

    conn->read.error = 1;
    return ret;
}


znx_result_t
znx_conn_write(znx_conn_t *conn, void *buf, size_t count)
{
    znx_result_t        ret;

    ret = znx_syscall_write(conn->fd, buf, count);

    if (ret > 0) {
        conn->sent_bytes += (size_t)ret;
        return ret;
    }

    conn->write.ready = 0;

    if (ret == 0) {
        conn->write.eof = 1;
        return 0;
    }

    if (-ret == EAGAIN) {
        return ZNX_AGAIN;
    }

    conn->write.error = 1;
    return ret;
}


znx_result_t
znx_conn_writev(znx_conn_t *conn, znx_iovec_t *vector, int count)
{
    znx_result_t        ret;

    ret = znx_syscall_writev(conn->fd, vector, count);

    if (ret > 0) {
        conn->sent_bytes += (size_t)ret;
        return ret;
    }

    conn->write.ready = 0;

    if (ret == 0) {
        conn->write.eof = 1;
        return ret;
    }

    if (-ret == EAGAIN) {
        return ZNX_AGAIN;
    }

    conn->write.error = 1;

    return ret;
}


znx_result_t
znx_conn_udp_recv(znx_conn_t *conn, void *buf, size_t count)
{
    znx_result_t        ret;

    ret = znx_syscall_recv(conn->fd, buf, count, 0);

    if (ret > 0) {
        conn->recv_bytes += (size_t)ret;
        return ret;
    }

    conn->read.ready = 0;

    if (ret == 0) {
        conn->read.eof = 1;
        return ret;
    }

    if (-ret != EAGAIN) {
        return ZNX_AGAIN;
    }

    conn->read.error = 1;

    return ret;
}


znx_result_t
znx_conn_udp_send(znx_conn_t *conn, void *buf, size_t count)
{
    znx_result_t        ret;

    ret = znx_syscall_send(conn->fd, buf, count, 0);

    if (ret > 0) {
        conn->sent_bytes += (size_t)ret;
        return ret;
    }

    conn->write.ready = 0;

    if (ret == 0) {
        conn->write.eof = 1;
        return ret;
    }

    if (-ret == EAGAIN) {
        return ZNX_AGAIN;
    }

    conn->write.eof = 1;

    return ret;
}
